home *** CD-ROM | disk | FTP | other *** search
/ L' Effet Pommier 3 / L'Effet Pommier - Volume 03.iso / Programmation / gray image 2.1 / conv_filter.cc < prev    next >
Text File  |  1995-05-31  |  9KB  |  284 lines

  1. // This may look like C code, but it is really -*- C++ -*-
  2. /*
  3.  ************************************************************************
  4.  *
  5.  *               Grayscale Image
  6.  *
  7.  *         Perform convolutions, linear FIR filtrations
  8.  *                of an image
  9.  *
  10.  * The present file implements several flavors of the linear FIR filtration,
  11.  * by rows, by columns, or a separable 2D filtration (which is filtration
  12.  * of rows followed by filtering the columns of an image). The file also
  13.  * contains the code for look-up table substitutions.
  14.  *
  15.  * The algorithm is simple, say for a 3-point FIR filter with coefficients
  16.  * [am1,a0,ap1], the pixels of a filtered image are computed as
  17.  *    new_pixel[l] = am1*pixel[l-1] + a0*pixel[l] + ap1*pixel[l+1]
  18.  * For row-wise filtration, pixel[l] = image(i,l) and we repeat this
  19.  * for all rows i; In column-wise filtration, pixel[l] = image(l,j)
  20.  * and we make image.q_ncols() that many filtration (separate for
  21.  * each column of the image, that is).
  22.  * When filtering window sticks out of the image, we extrapolate
  23.  * the corresponding pixels. That is, we assume pixel[-1] = pixel[0]
  24.  * and pixel[lmax+1] = pixel[lmax]
  25.  *
  26.  * All filtration is done in-place!
  27.  *
  28.  * $Id: conv_filter.cc,v 2.0 1995/03/17 17:20:43 oleg Exp oleg $
  29.  *
  30.  ************************************************************************
  31.  */
  32.  
  33. #ifdef __GNUC__
  34. #pragma implementation "filter.h"
  35. #endif
  36. #include "filter.h"
  37.  
  38. #include <minmax.h>
  39.  
  40.                 // Expand the templates right here...
  41. template class ConvKernel3<CommonDenomOne>;
  42. template class ConvKernel3<over_2_up>;
  43. template class ConvKernel3<CommonDenom>;
  44.  
  45. /*
  46.  *------------------------------------------------------------------------
  47.  *            Simple 3-point convolutions
  48.  */
  49.  
  50.  
  51.                 // 3-point row-wise convolutions
  52. #define CONV3(Denom)                            \
  53.                                     \
  54. IMAGE& FilterIt::conv_row(const ConvKernel3<Denom>& kernel)        \
  55. {                                    \
  56.   if( image.ncols < 2 )                            \
  57.     image.info(),                            \
  58.     _error("we need at least 3 columns for a 3-point row convolution"); \
  59.                                     \
  60.   register GRAY_SIGNED * pp = (GRAY_SIGNED *)image.pixels;        \
  61.   register GRAY_SIGNED prev_pixel;    /* in the row         */    \
  62.                                     \
  63.   for(GRAY_SIGNED * row_end = (GRAY_SIGNED *)image.pixels + image.ncols; \
  64.       row_end <= (GRAY_SIGNED *)image.pixels + image.npixels;        \
  65.       row_end += image.ncols)                        \
  66.   {                                    \
  67.     prev_pixel = *pp;            /* Assume pixel[-1]=pixel[0] */    \
  68.                                     \
  69.     while(pp < row_end-1)                        \
  70.     {                                    \
  71.       GRAY_SIGNED new_pixel = kernel.convolve(prev_pixel,pp[0],pp[1]);    \
  72.       prev_pixel = pp[0];                        \
  73.       *pp++ = new_pixel;                        \
  74.     }                                    \
  75.     *pp++ = kernel.convolve(prev_pixel,pp[0],pp[0]);            \
  76.   }                                    \
  77.   assert( pp == (GRAY_SIGNED *)image.pixels + image.npixels );        \
  78.   return image;                                \
  79. }                                    \
  80.                                     \
  81.                 /* 3-point col-wise convolution */    \
  82. IMAGE& FilterIt::conv_col(const ConvKernel3<Denom>& kernel)        \
  83. {                                    \
  84.   if( image.nrows < 2 )                            \
  85.     image.info(),                            \
  86.     _error("we need at least 3 rows for a 3-point col convolution");    \
  87.                                     \
  88.   register const int ncols = image.ncols;    /* caching */        \
  89.   const int fl_jump = image.npixels - ncols;    /* between 1. and last rows */\
  90.   register GRAY_SIGNED * pp = (GRAY_SIGNED *)image.pixels;        \
  91.   register GRAY_SIGNED prev_pixel;    /* in the col */        \
  92.                                     \
  93.   GRAY_SIGNED * last_row = (GRAY_SIGNED *)image.pixels + fl_jump;    \
  94.   for(; last_row < (GRAY_SIGNED *)image.pixels + image.npixels;        \
  95.       last_row++)                             \
  96.   {                                    \
  97.     prev_pixel = *pp;            /* Assume pixel[-1]=pixel[0] */    \
  98.                                     \
  99.     for(; pp < last_row; pp += ncols)                    \
  100.     {                                    \
  101.       GRAY_SIGNED new_pixel = kernel.convolve(prev_pixel,pp[0],pp[ncols]); \
  102.       prev_pixel = pp[0];                        \
  103.       pp[0] = new_pixel;                        \
  104.     }                                    \
  105.     pp[0] = kernel.convolve(prev_pixel,pp[0],pp[0]);            \
  106.     pp -= fl_jump-1;            /* Jump over the next col */    \
  107.   }                                    \
  108.   assert( pp == last_row - fl_jump );                    \
  109.   return image;                                \
  110. }                                    \
  111.  
  112.  
  113. CONV3(CommonDenomOne)
  114. CONV3(over_2_up)
  115. CONV3(CommonDenom)
  116.  
  117. #undef CONV3
  118.  
  119. //------------------------------------------------------------------------
  120. //              Routing filtration module
  121. // pick up and execute the most efficient method given input parameters
  122. //
  123. // I wish I could use templates, but theu suck! I can't declare templates
  124. // inside classes, w/o making an entire type templated
  125. // I can't help but resort to a good old c preprocessor
  126.  
  127. #define CONV_ROUTER(Denom)                    \
  128.                                 \
  129. IMAGE& FilterIt::conv(const ConvKernel3<Denom>& kernel,const Direction how) \
  130. {                                \
  131.   image.is_valid();                        \
  132.                                 \
  133.   switch(how)                            \
  134.   {                                \
  135.     case RowsAndColumns:                    \
  136.          return conv_row(kernel), conv_col(kernel);        \
  137.     case Columns:                        \
  138.      return conv_col(kernel);                \
  139.     case Rows:                            \
  140.      return conv_row(kernel);                \
  141.   }                                \
  142.   _error("There is no way we can reach here...");        \
  143.   return image;                /* just to make the compiler happy */\
  144. }                                \
  145.  
  146.  
  147. CONV_ROUTER(CommonDenomOne)
  148. CONV_ROUTER(over_2_up)
  149. CONV_ROUTER(CommonDenom)
  150.  
  151. #undef CONV_ROUTER
  152.  
  153. /*
  154.  *------------------------------------------------------------------------
  155.  *                 Table Look-ups
  156.  */
  157.  
  158.                 // Allocate an empty look-up table
  159. void LookupT::allocate(const GRAY _min_val, const GRAY _max_val)
  160. {
  161.   assure(_max_val >= _min_val, "bad range allocating the LookUp table");
  162.   max_val = _max_val;
  163.   min_val = _min_val;
  164.   no_entries = max_val - min_val + 1;
  165.   if( no_entries > Preallocated_no )
  166.     table = new GRAY[no_entries];
  167.   else                    // Use storage preallocated within
  168.     table = preallocated;        // the object whenever possible
  169. }
  170.  
  171.  
  172.  
  173.                 // Copy constructor
  174. LookupT::LookupT(const LookupT& another)
  175. {
  176.   allocate(another.min_val,another.max_val);
  177.   *this = another;
  178. }
  179.  
  180.                 // It's OK to assign one LookupT to another
  181.                 // But they have to be similar
  182. LookupT& LookupT::operator= (const LookupT& another)
  183. {
  184.   if( min_val != another.min_val || max_val != another.max_val )
  185.     _error("Can't assign [%d,%d]-range look-up table to [%d,%d] one",
  186.        another.min_val, another.max_val, min_val, max_val );
  187.   assert( no_entries == another.no_entries );
  188.   memcpy(table,another.table,sizeof(table[0])*no_entries);
  189.   return *this;
  190. }
  191.  
  192.                 // Destructor
  193. LookupT::~LookupT(void)
  194. {
  195.   assert( table != 0 && no_entries > 0 && max_val >= min_val );
  196.   if( table != preallocated )
  197.     delete [] table;
  198.   table = 0;
  199. }
  200.  
  201.  
  202.                 // Build a table for an identical pixel
  203.                 // mapping for a specified depth image
  204. LookupT::LookupT(const Identical id)
  205. {
  206.   assure(id.depth > 0 && id.depth < 14,
  207.      "a fishy depth to build a lookup table for");
  208.   allocate(0,(1<<id.depth)-1);
  209.   for(register GRAY i=min_val; i<=max_val; i++)
  210.     table[i-min_val] = i;
  211. }
  212.  
  213.  
  214.                 // Build a table to map just one
  215.                 // particular pixel
  216. LookupT::LookupT(const MapTo map_one)
  217. {
  218.   allocate(map_one.from,map_one.from);
  219.   (*this)[map_one.from] = map_one.to;
  220. }
  221.  
  222.                 // Build a table to reduce image depth from
  223.                 // one value to another (that is, build a
  224.                 // stripy table)
  225. LookupT::LookupT(const DownGRAY downgray)
  226. {
  227.   assure(downgray.depth_from > 0 && downgray.depth_from < 14 &&
  228.      downgray.depth_from >= downgray.depth_to,
  229.      "a fishy depths to build a downgray lookup table for");
  230.   allocate(0,(1<<downgray.depth_from)-1);
  231.   const card downgr_factor = 1<<(downgray.depth_from-downgray.depth_to);
  232.   register GRAY * tp = table;
  233.   register GRAY val;
  234.   for(val=0; tp < table + no_entries; val++)
  235.     for(register int i=0; i<downgr_factor; i++)
  236.       *tp++ = val;
  237.   assert( tp == table + no_entries && val == 1<<downgray.depth_to );
  238. }
  239.  
  240.                 // Convolve two Lookup tables
  241. LookupT& LookupT::apply(const LookupT& another, 
  242.             const FringeHandling fringe_handling)
  243. {
  244.   register GRAY_SIGNED * tp;
  245.   if( fringe_handling == CoerceFringes )
  246.     for(tp=(GRAY_SIGNED *)table; tp < (GRAY_SIGNED *)table + no_entries; tp++)
  247.       *tp = another[min(max(*tp,(GRAY_SIGNED)another.min_val),
  248.                   (GRAY_SIGNED)another.max_val)];
  249.   else                    // Leave fringes alone
  250.     for(tp=(GRAY_SIGNED *)table; tp < (GRAY_SIGNED *)table + no_entries; tp++)
  251.       if( *tp >= another.min_val && *tp <= another.max_val )
  252.     *tp = another[*tp];
  253.   return *this;
  254. }
  255.  
  256.                 // Translate image pixels according to
  257.                 // the look-up table
  258. IMAGE& FilterIt::translate(const LookupT& lookupt,
  259.                const LookupT::FringeHandling fringe_handling)
  260. {
  261.   register GRAY_SIGNED * pp = (GRAY_SIGNED *)image.pixels;
  262.   if( fringe_handling == LookupT::CoerceFringes )
  263.     while( pp < (GRAY_SIGNED *)image.pixels + image.npixels )
  264.     {
  265.       int offset = *pp - lookupt.min_val;
  266.       if( offset < 0 )
  267.     offset = 0;
  268.       else if( offset >= lookupt.no_entries )
  269.     offset = lookupt.no_entries - 1;
  270.       *pp++ = lookupt.table[offset];
  271.     }
  272.   else                    // Leave fringes alone
  273.     for(; pp < (GRAY_SIGNED *)image.pixels + image.npixels; pp++)
  274.     {
  275.       int offset = *pp - lookupt.min_val;
  276.       if( offset >= 0 && offset < lookupt.no_entries )
  277.     *pp = lookupt.table[offset];
  278.     }
  279.  
  280.   return image;
  281. }
  282.  
  283.  
  284.